package nl.cypherpunk.statefuzzer;

import nl.cypherpunk.statefuzzer.tls.TLSTestService;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

public class DiffReplay {
    //paragrams parse
    //args[0] : openssl
    //args[1] : port(4433)
    //args[2] : seed directory path
    //args[3] : target command
    //args[4] : client or server
    //args[5] : /home/pany/test/libgcov_preload.so
    //openssl 4333 ClientDiff "/home/pany/test/openssl/apps/openssl s_client -key server.key -cert server.crt -CAfile cacert.pem -tls1_2 -connect 127.0.0.1:4333" client /home/pany/test/libgcov_preload.so
    //openssl 4433 ClientDiff "/home/pany/test/openssl_1_1_1h/apps/openssl s_client -key server.key -cert server.crt -CAfile cacert.pem -tls1_2 -connect 127.0.0.1:4433" client /home/pany/test/libgcov_preload.so
    //openssl 4443 ClientDiff "/home/pany/test/libressl-3.2.1/build/bin/openssl s_client -key server.key -cert server.crt -CAfile cacert.pem -tls1_2 -connect 127.0.0.1:4443" client
    //openssl 4443 ClientDiff "sudo /home/pany/test/libressl3.4.0/apps/openssl/openssl s_client -key server.key -cert server.crt -CAfile cacert.pem -tls1_2  -connect 127.0.0.1:4443" client
    public static void main(String[] args) throws Exception {

        if (args.length >= 3) {
            TLSTestService tls = new TLSTestService();
            //tls.setOutput(true);
            tls.setOutputDir(args[0]);
            tls.setTarget(args[4]);
            tls.setHost("127.0.0.1");
            tls.setPort(Integer.valueOf(args[1]));
            tls.setCommand(args[3]);
            tls.setReceiveMessagesTimeout(100);
            tls.setConsoleOutput(true);
            tls.setRestartTarget(true);
            //tls.setEnvCmd(args[5]);
            tls.start();
            tls.useTLS12();
            File file = new File(args[2]);
            File[] tempList = file.listFiles();
            List fileList = Arrays.asList(tempList);
            Collections.sort(fileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    if (o1.isDirectory() && o2.isFile())
                        return -1;
                    if (o1.isFile() && o2.isDirectory())
                        return 1;
                    Path path1 = Paths.get(o1.getPath());
                    BasicFileAttributeView basicview1 = Files.getFileAttributeView(path1, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
                    BasicFileAttributes attr1 = null;
                    Path path2 = Paths.get(o2.getPath());
                    BasicFileAttributeView basicview2 = Files.getFileAttributeView(path2, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
                    BasicFileAttributes attr2 = null;
                    try
                    {
                        attr1 = basicview1.readAttributes();
                        attr2 = basicview2.readAttributes();
                    }catch (Exception e)
                    {

                    }

                    return attr1.creationTime().compareTo(attr2.creationTime());
                }
            });

            int totalNum = 0;
            for (File file1 : tempList) {
                File temp = new File(file1.getPath());
                System.out.println(file1.getName());
                totalNum++;
                try {
                    InputStream fileIn = new FileInputStream(temp);
                    //DataInputStream in = new DataInputStream(fileIn);

                    // 使用缓存区读入对象效率更快
                    BufferedInputStream in = new BufferedInputStream(fileIn);
                    byte[] len = new byte[1024];
                    int length;
                    List<byte[]> seed = new ArrayList<>();
                    tls.reset();
                    while (in.read(len, 0, 4) != -1) {
                        length = byteArrayToInt(len);
                        byte[] bys = new byte[length];
                        in.read(bys, 0, length);
                        try {
                            String output = tls.sendFuzzingMessage(bys);
                            if (output.contains("Closed"))
                                break;
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                        seed.add(bys);
                    }
                    in.close();
                    fileIn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                /*
                if(totalNum % step == 0 || totalNum == tempList.length || timestamp > endTime){
                    List<String> cmdList = new ArrayList<>();
                    cmdList.add("/bin/sh");
                    cmdList.add("-cf");
                    cmdList.add(args[4]);
                    ProcessBuilder pb = new ProcessBuilder(cmdList);
                    pb.redirectErrorStream(true);
                    Process process = pb.start();
                    int coverage = readProcessOutput(process.getInputStream(), "coverage.txt", timestamp);
                    process.waitFor();
                    process.exitValue();
                }*/
            }
            tls.closeSocket();
            tls.close();
            return;
        }


    }
    private static Integer readProcessOutput(InputStream inputStream, String filename, Long seedTime) throws
            FileNotFoundException {
        PrintStream log = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename, true)));

        Double[] coverage = new Double[4];
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("GBK")));
            String line;

            if ((line = reader.readLine()) != null) {
                //System.out.println(line);
                String[] tmp = line.split(" ");
                coverage[0] = Double.parseDouble(tmp[1].substring(0, tmp[1].length() - 1));
                coverage[1] = Double.parseDouble(tmp[2].substring(1));
                coverage[2] = Double.parseDouble(tmp[7].substring(0, tmp[7].length() - 1));
                coverage[3] = Double.parseDouble(tmp[8].substring(1));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.print(seedTime);
        log.print(" ");
        log.print(coverage[0]);
        log.print(" ");
        log.print(coverage[1]);
        log.print(" ");
        log.print(coverage[2]);
        log.print(" ");
        log.println(coverage[3]);
        log.close();
        return coverage[3].intValue();
    }

    public static int byteArrayToInt(byte[] bytes) {
        int value=0;
        for(int i = 0; i < 4; i++) {
            int shift= i * 8;
            value +=(bytes[i] & 0xFF) << shift;
        }
        return value;
    }
}

